//Shkolnij Rostislav
package CalculationCore;

import java.util.Vector;

public class CCPUStack {
	private Vector<CCPURegister> vecX; //for limited (X, Y, Z, T) and unlimited stack
	private CCPURegister auxX;	  //X1 for limited (X, Y, Z, T) stack
	private boolean IsRealMode;
	
	public CCPUStack(int depth)
	{
		if(depth > 0)
		{

			auxX = new CCPURegister();
			
			vecX = new Vector<CCPURegister>(depth);
			for(int i = 0; i<depth; i++)
				vecX.add(new CCPURegister());

			if(depth == 4)
				IsRealMode = true;
			else
				IsRealMode = false;
		}
		else 
			System.out.println(" !!! depth must be > 0");
	}
	
	public CCPUStack(double[] src)
	{
		int depth = src.length;
		if(depth > 0)
		{
			auxX = new CCPURegister(src[0]);
			
			vecX = new Vector<CCPURegister>(depth);
			for(int i = 1; i<depth; i++)
				vecX.add(new CCPURegister(src[i]));

			if(depth == 4)
				IsRealMode = true;
			else
				IsRealMode = false;
		}
		else 
			System.out.println(" !!! depth must be > 0");
	}
	
	public int getSize()
	{
		return vecX.size();
	}
	
	public Vector<CCPURegister> getAllRegisters()
	{
		Vector<CCPURegister> res = new Vector<CCPURegister>(5);
		for(int i = 0; i<vecX.size(); i++)
			res.add(vecX.get(i));
		return res;
	}
	
	public boolean compareAllRegisters(double[] src, double rounding)
	{
		boolean res = true;
		if(src.length == this.vecX.size() + 1)	{
			res = this.auxX.isTheSameValue(src[0], rounding);
			for(int i = 1; i<src.length && res; i++) {
				res = this.vecX.get(i-1).isTheSameValue(src[i], rounding);
			}
			return res; 
		} else {
			System.out.println(String.format(" !!! Src length must be = %d", this.vecX.size()));
			return false; 
		}
	}
	
	/*public void setAllRegisters(double[] src)
	{
		if(src.length == this.vecX.size())	{
			for(int i = 0; i<src.length; i++)
				this.vecX.set(i, new CCPURegister(src[i]));
		} else {
			System.out.println(String.format(" !!! Src length must be = %d", this.vecX.size()));
		}
	}*/
	
	public void setAllRegisters(double[] src)
	{
		if(src.length == this.vecX.size() + 1)	{
			this.setAuxX(src[0]);
			for(int i = 1; i<src.length + 1; i++)
				this.vecX.set(i, new CCPURegister(src[i]));
		} else {
			System.out.println(String.format(" !!! Src length must be = %d", this.vecX.size()));
		}
	}
	
	public void setX0(int index, CCPURegister src) {
		vecX.set(index, src);
	}
		
	public void setX0(int index, double src) {
		CCPURegister temp = vecX.get(index);
		temp.setdFullNumber(src);
		vecX.set(index, temp);
	}
	
	public CCPURegister getXbyIndex(int index) {
		if(index >= 0 &&  index < vecX.size())
			return vecX.get(index);
		else
		{
			System.out.println(String.format("!!! Index %d is out of range", index));
			return null;
		}
	}
	
	public double[] getDoubleArray() {
		double[] res = new double[this.vecX.size() + 1];
		
		res[0] = this.auxX.getdFullNumber();
		
		for(int i=1; i<this.vecX.size()+1; i++)
			res[i] = vecX.get(i-1).getdFullNumber();
		return res;
	}
	
	public void setDoubleArray(double[] src) {
		if(src.length != vecX.size() + 1){
			System.out.println(String.format(" !!! Src length must be = %d", this.vecX.size() + 1));
		} else {
			auxX.setdFullNumber(src[0]);
			for(int i=1; i<vecX.size()+1; i++)
				vecX.get(i-1).setdFullNumber(src[i]);
		}
	}
	
	public void setXbyIndex(int index, CCPURegister src) {
		if(index >= 0 &&  index < vecX.size())
			vecX.set(index, src);
		else
			System.out.println(String.format("!!! Index %d is out of range", index));
	}
	
	public void setAuxX(CCPURegister src) {
		auxX.copy(src);
	}
	
	public void setAuxX(double src) {
		auxX.setdFullNumber(src);
	}
	
	public CCPURegister getAuxX() {
			return auxX;
	}

	public void setIsRealMode(boolean isRealMode) {
		IsRealMode = isRealMode;
	}

	public boolean hasRealMode() {
		return IsRealMode;
	}
	
	public void insertDigitInXmantissa(int digit) {
		vecX.get(0).insertDigitInMantissa(digit);
	}
	
	public void insertCommaInXmantissa() {
		vecX.get(0).insertComma();
	}
	
	public void insertExponentaInXmantissa() {
		vecX.get(0).insertExponenta();
	}
	
	public void insertMinusInXmantissa()	{
		vecX.get(0).insertMinusInMantissa();
	}
	
	public void insertDigitInXexponenta(int digit) {
		vecX.get(0).insertDigitInExponenta(digit);
	}
	
	public void insertMinusInXexponenta()	{
		vecX.get(0).insertMinusInExponenta();
	}
	
	public void cleanupX0()	{
		vecX.get(0).cleanup();
	}
	
	public void cleanupX0exponenta() {
		vecX.get(0).cleanupExponenta();
	}
	
	public void cleanup()	{
		
		auxX.cleanup();
		
		for(int i=0; i<vecX.size(); i++)
			vecX.get(i).cleanup();
	}
	
	public void regPush()	{
		CCPURegister temp = new CCPURegister(vecX.get(0));
		CCPURegister temp1;

		for(int i=0; i<vecX.size()-1; i++) // X->Y->Z->T, 0.0->X. 
		{
			temp1 = vecX.get(i+1);
			vecX.set(i+1, temp);
			temp = temp1;
		}//last temp1 is lost
		
		vecX.get(0).cleanup();
	}
	
	public void regPop()	{
		CCPURegister temp = new CCPURegister(vecX.get(vecX.size()-1));
		CCPURegister temp1;
		
		for(int i=vecX.size()-1; i>=0; i--) // T->Z->Y->X, T->T. 
		{
			temp1 = vecX.get(i);
			vecX.set(i, temp);
			temp = temp1;
		}

	}
	
	public void regRotate()
	{
		CCPURegister temp = new CCPURegister(auxX);
		CCPURegister temp1;
		
		auxX = new CCPURegister(vecX.get(0));

		for(int i=vecX.size()-1; i>=0; i--) // T->Z->Y->X, X->X1, X1->T. 
		{
			temp1 = vecX.get(i);
			vecX.set(i, temp);
			temp = temp1;
		}
	}
	
	public void regRecallXfromX1()
	{
		CCPURegister temp = new CCPURegister(auxX);
		vecX.set(0, temp);
	}
	
	public void regBackupXintoX1()
	{
		auxX.setdFullNumber(vecX.get(0).getdFullNumber());
	}
	
	public void operSum()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = new CCPURegister(vecX.get(0));
		CCPURegister temp2 = new CCPURegister(vecX.get(1));
		temp1.setdFullNumber(temp1.getdFullNumber() + temp2.getdFullNumber());
		
		this.regPop();
		
		vecX.set(0, temp1);
	}
	public void operSubtract()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = new CCPURegister(vecX.get(0));
		CCPURegister temp2 = new CCPURegister(vecX.get(1));
		temp1.setdFullNumber(temp2.getdFullNumber() - temp1.getdFullNumber());
		
		this.regPop();
		
		vecX.set(0, temp1);
	}
	public void operMultiply()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = new CCPURegister(vecX.get(0));
		CCPURegister temp2 = new CCPURegister(vecX.get(1));
		temp1.setdFullNumber(temp1.getdFullNumber() * temp2.getdFullNumber());
		
		this.regPop();
		
		vecX.set(0, temp1);
	}
	public boolean operDivide()
	{
		CCPURegister temp1 = new CCPURegister(vecX.get(0));
		CCPURegister temp2 = new CCPURegister(vecX.get(1));
		
		if(temp1.getdFullNumber() == 0.0){
			systemErrorMessage();
			return true;
		} else {
			auxX = new CCPURegister(vecX.get(0));

			temp1.setdFullNumber(temp2.getdFullNumber() / temp1.getdFullNumber());
			
			this.regPop();
			
			vecX.set(0, temp1);
			
			return false;
		}
	}
	public void regExchangeXY()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp = vecX.get(1);
		vecX.set(1, vecX.get(0));
		vecX.set(0, temp);
	}
	public void oper10pow()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.pow(10, temp1.getdFullNumber()));
		
	}
	public void operEpow()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.exp(temp1.getdFullNumber()));
	}
	public boolean operLg()
	{
		CCPURegister temp1 = vecX.get(0);
		
		if(temp1.getdFullNumber() <= 0.0)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			if(temp1.getdFullNumber() == 0)
				systemErrorMessage();
			else
				temp1.setdFullNumber(Math.log10(temp1.getdFullNumber()));
			return false;
		}
	}
	public boolean operLn()
	{
		CCPURegister temp1 = vecX.get(0);
		
		if(temp1.getdFullNumber() <= 0.0)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			if(temp1.getdFullNumber() == 0)
				systemErrorMessage();
			else
				temp1.setdFullNumber(Math.log(temp1.getdFullNumber()));
			return false;
		}
	}
	public boolean operArcsin(boolean isRadian_NotDegree)
	{
		CCPURegister temp1 = vecX.get(0);
		
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = 180.0/Math.PI;
		
		if(Math.abs(temp1.getdFullNumber()) > 1)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			if(Math.abs(temp1.getdFullNumber()) > 1.0)
				systemErrorMessage();
			else
				temp1.setdFullNumber(Math.asin(temp1.getdFullNumber())*mult);
			return false;
		}
	}
	public boolean operArccos(boolean isRadian_NotDegree) 
	{
		CCPURegister temp1 = vecX.get(0);
		
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = 180.0/Math.PI;
		
		if(Math.abs(temp1.getdFullNumber()) > 1)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			if(Math.abs(temp1.getdFullNumber()) > 1.0)
				systemErrorMessage();
			else
				temp1.setdFullNumber(Math.acos(temp1.getdFullNumber())*mult);
			return false;
		}
	}
	public void operArctg(boolean isRadian_NotDegree) 
	{
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = 180.0/Math.PI;
		
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.atan(temp1.getdFullNumber())*mult);
	}
	public void operSin(boolean isRadian_NotDegree) 
	{
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = Math.PI/180.0;
		
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.sin(temp1.getdFullNumber()*mult));
	}
	public void operCos(boolean isRadian_NotDegree) 
	{
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = Math.PI/180.0;
		
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.cos(temp1.getdFullNumber()*mult));
	}
	public boolean operTg(boolean isRadian_NotDegree) 
	{
		double mult;
		if(isRadian_NotDegree)
			mult = 1;
		else
			mult = Math.PI/180.0;
		
		CCPURegister temp1 = vecX.get(0);
		
		if(Math.abs(temp1.getdFullNumber() % Math.PI) == Math.PI/2.0)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));
			
			temp1.setdFullNumber(Math.tan(temp1.getdFullNumber()*mult));
			return false;
		}
	}
	public void operPi()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.PI);
	}
	public void operSqr()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.sqrt(temp1.getdFullNumber()));
	}
	public void operXpow2()
	{
		auxX = new CCPURegister(vecX.get(0));
		CCPURegister temp1 = vecX.get(0);
		temp1.setdFullNumber(Math.pow(temp1.getdFullNumber(),2));
	}
	public boolean oper1divX()
	{
		CCPURegister temp1 = vecX.get(0);
		
		if(temp1.getdFullNumber() == 0.0)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			if(temp1.getdFullNumber() == 0)
				systemErrorMessage();
			else
				temp1.setdFullNumber(1.0/temp1.getdFullNumber());
			return false;
		}
	}
	public boolean operXpowY()
	{
		CCPURegister temp1 = vecX.get(0);
		CCPURegister temp2 = vecX.get(1);
		
		if(temp1.getdFullNumber() == 0.0)
			return true;
		else
		{
			auxX = new CCPURegister(vecX.get(0));

			temp1.setdFullNumber(Math.pow(temp1.getdFullNumber(), temp2.getdFullNumber()));
			
			this.regPop();
			
			vecX.set(0, temp1);
			return false;
		}
	}

	public void systemOutput(char c)
	{
		System.out.println(String.format(" Stack :"));
		System.out.print(String.format("aux%c:", c));
		auxX.systemOutput();

		for(int i=0; i<vecX.size(); i++)
		{
			System.out.print(String.format("%c%d  :", c, i));
			vecX.get(i).systemOutput();
		}
	}
	
	public void systemErrorMessage(){
		System.out.println(String.format(" !!! Error"));
	}
	

}
